home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / ThreadLibrary 1.3 / Source / Demos / ThreadsTimed / ThreadsTimed.c < prev    next >
Text File  |  1995-09-20  |  10KB  |  325 lines

  1. /* See the file Distribution for distribution terms.
  2.     (c) Copyright 1994 Ari Halberstadt */
  3.  
  4. /*    This program compares the speed of my Thread Library with Apple's
  5.     Thread Manager. I have tried to be careful to set the tests up
  6.     so that they are equally fair to Thread Library and to Thread
  7.     Manager.
  8.     
  9.     The most important aspect of efficiency for threads is the speed of
  10.     context switches. Other thread operations are usually done only
  11.     occasionally. For instance, the time spent in the routines used to
  12.     allocate and dispose of threads tends to be much less than the time
  13.     spent executing threads, and the number of calls to create and
  14.     dispose of threads also tends to be much smaller than the number
  15.     of calls to the context switching routines. Thus, a test program
  16.     that is used to measure the speed of an implementation of threads would
  17.     do best to measure the speed of context switches.
  18.     
  19.     Context switches in Thread Library are accomplished using the routine
  20.     ThreadYield, while most context switches using Thread Manager would
  21.     be done using the routine YieldToAnyThread. To measure the relative speed
  22.     of context switches, this program sets up several threads. Each thread
  23.     runs in an infinite loop, in each iteration of which a global counter is
  24.     incremented and then the appropriate context switching routine is called.
  25.     The threads are executed for a predetermined time; when the time has
  26.     elapsed, the threads are all disposed of. The greater the number of context
  27.     switches that were accomplished during the run time, the larger the value
  28.     the counter will have reached. Thus, we can determine which implementation
  29.     has a shorter scheduling and context switching time by comparing the maximum
  30.     values of the counters. For comparison, a test using a fairly simple "for"
  31.     loop is run to determine the approximate maximum theoretical value that the
  32.     counter could reach if the context switch time were zero.
  33.     
  34.     Usually threads will do more than these simple test threads, so the time
  35.     spent in context switches will be a smaller portion of the total time spent
  36.     in the program. It is still better, however, to have a faster context
  37.     switch time.
  38.     
  39.     The following output was produced on 94/03/01 on a Macintosh Plus running
  40.     System 7.0 and Thread Manager 1.2. All other extensions were disabled.
  41.     The only other open application was Finder 7.0. Thread Library 1.0d3
  42.     was used in the tests. All optimizations and smart link were enabled,
  43.     and all debug code was disabled (by defining NDEBUG) when the test
  44.     program and Thread Library were compiled using THINK C 5.0.4. The
  45.     program executed as an application (which is faster than running under
  46.     the THINK C debugger).
  47.  
  48.         Thread Library: count = 135377 (ThreadYield was called 143839 times)
  49.         Thread Manager: count = 52704 (YieldToAnyThread was called 55998 times)
  50.         No threads: count = 4043808
  51.  
  52.     In these tests, Thread Library is about 2.6 times faster than Thread
  53.     Manager. The approximate maximum possible count value is 4043808, so
  54.     the efficiency of Thread Library is about 3.3% of that of a simple
  55.     loop, while the efficiency of Thread Manager is about 1.3% of the
  56.     efficiency of the same loop.
  57.         
  58.     The first time I compared Thread Library to Thread Manager, I was
  59.     surprised to find that Thread Library was at least 32% faster than
  60.     Thread Manager. After some performance tuning, Thread Library is
  61.     now 2 to 3 times faster than Thread Manager. If even this is too slow,
  62.     you could modify Thread Library to be even more efficient, perhaps by
  63.     coding critical sections in assembly language. In contrast, Apple's
  64.     Thread Manager can not be modified by the end user. One would expect
  65.     an operating system function produced by Apple to be at least competitive
  66.     in its speed, but for some reason Thread Manager is quite slow.
  67.     
  68.     94/07/15 aih - uses unsigned long for counter
  69.     94/03/01 aih - removed test for system version
  70.                      - updated results
  71.     94/02/18 aih - errors are reported using printf instead of DebugStr
  72.     94/02/17 aih - release 1.0d2
  73.                      - made Thread Library faster
  74.     94/02/15 aih - added comparison with Thread Manager
  75.                      - added big header comment
  76.     94/02/13 aih - created */
  77.  
  78. #include <limits.h>
  79. #include <stdio.h>
  80. #include <string.h>
  81. #include <Desk.h>
  82. #include <Dialogs.h>
  83. #include <Events.h>
  84. #include <Fonts.h>
  85. #include <GestaltEqu.h>
  86. #include <LowMem.h>
  87. #include <Memory.h>
  88. #include <Menus.h>
  89. #include <QuickDraw.h>
  90. #include <OSEvents.h>
  91. #include <OSUtils.h>
  92. #include <Resources.h>
  93. #include <SegLoad.h>
  94. #include <TextUtils.h>
  95. #include <ToolUtils.h>
  96. #include <Threads.h>
  97. #include "ThreadLibrary.h"
  98.  
  99. #define NTESTS        (3)        /* number of tests executed */
  100. #define NTHREADS    (16)        /* number of threads to create */
  101. #define RUNSECS    (10L)        /* number of seconds to run each test */
  102. #define RUNTICKS    (RUNSECS * THREAD_TICKS_SEC)    /* time to run threads */
  103.  
  104. typedef unsigned long count_t;
  105.  
  106. /* data passed to threads */
  107. typedef struct {
  108.     count_t yield;                /* number of times yield function was called */
  109.     count_t count;                /* counter incremented every time a thread is run */
  110. } DataType;
  111.  
  112. /* initialize application heap */
  113. static void HeapInit(long stack, short masters)
  114. {
  115.     SetApplLimit(GetApplLimit() - stack);
  116.     MaxApplZone();
  117.     while (masters-- > 0)
  118.         MoreMasters();
  119. }
  120.  
  121. /* initialize managers */
  122. static void ManagersInit(void)
  123. {
  124.     EventRecord event;
  125.     short i;
  126.     
  127.     /* standard initializations */
  128.     InitGraf((Ptr) &qd.thePort);
  129.     InitFonts();
  130.     InitWindows();
  131.     InitMenus();
  132.     TEInit();
  133.     InitDialogs(0);
  134.     FlushEvents(everyEvent, 0);
  135.     InitCursor();
  136.     
  137.     /* so first window will be frontmost */
  138.     for (i = 0; i < 4; i++)
  139.         EventAvail(everyEvent, &event);
  140. }
  141.  
  142. /* print an error message and die */
  143. static void fatal(const char *msg, OSErr err)
  144. {
  145.     printf("Fatal error #%d: %s.\n", err, msg);
  146.     ExitToShell();
  147. }
  148.  
  149. /* a simple thread that uses Thread Library */
  150. static void tl_thread(void *data)
  151. {
  152.     register DataType *td = data;
  153.     
  154.     for (;;) {
  155.         td->count++;
  156.         td->yield++;
  157.         ThreadYield(0);
  158.     }
  159. }
  160.  
  161. /* a simple thread that uses Thread Manager */
  162. static pascal void *tm_thread(void *data)
  163. {
  164.     register DataType *td = data;
  165.     
  166.     for (;;) {
  167.         td->count++;
  168.         td->yield++;
  169.         YieldToAnyThread();
  170.     }
  171.     return(NULL);
  172. }
  173.  
  174. /* a very simple non-thread that does approximately the same thing as
  175.     the threads */
  176. static count_t nt_thread(void)
  177. {
  178.     ThreadTicksType nextEvent;
  179.     ThreadTicksType stop;
  180.     count_t count;
  181.     short i;
  182.  
  183.     /* run for a predetermined number of ticks */
  184.     count = 0;
  185.     nextEvent = 0;
  186.     stop = TickCount() + RUNTICKS;
  187.     while (TickCount() < stop) {
  188.     
  189.         /* periodically discard all pending events */
  190.         if (TickCount() >= nextEvent) {
  191.             FlushEvents(everyEvent, 0);            
  192.             nextEvent = TickCount() + THREAD_TICKS_SEC;
  193.         }
  194.  
  195.         /* simulate the action of several threads, but without the overhead of
  196.             the thread dispatcher */
  197.         for (i = 0; i < NTHREADS; i++)
  198.             count++;
  199.     }
  200.     return(count);
  201. }
  202.  
  203. /* test ThreadLib */
  204. static void tl_test(void)
  205. {
  206.     ThreadType threads[NTHREADS];
  207.     DataType td;
  208.     ThreadTicksType nextEvent;
  209.     ThreadTicksType stop;
  210.     short i;
  211.     
  212.     printf("\nTesting Thread Library. This will take %ld seconds.\n", RUNSECS);
  213.  
  214.     /* create main thread */
  215.     if (! ThreadBeginMain(NULL, NULL, NULL))
  216.         fatal("can't create main thread using Thread Library", ThreadError());
  217.     
  218.     /* create several threads */
  219.     memset(&td, 0, sizeof(DataType));
  220.     for (i = 0; i < NTHREADS; i++) {
  221.         threads[i] = ThreadBegin(tl_thread, NULL, NULL, &td, 0);
  222.         if (! threads[i])
  223.             fatal("can't create thread using Thread Library", ThreadError());
  224.     }
  225.     
  226.     /* run for a predetermined number of ticks */
  227.     nextEvent = 0;
  228.     stop = TickCount() + RUNTICKS;
  229.     while (TickCount() < stop) {
  230.  
  231.         /* periodically discard all pending events */
  232.         if (TickCount() >= nextEvent) {
  233.             FlushEvents(everyEvent, 0);            
  234.             nextEvent = TickCount() + THREAD_TICKS_SEC;
  235.         }
  236.  
  237.         /* switch to another thread */
  238.         td.yield++;
  239.         ThreadYield(0); /* must be zero for fair comparison with thread manager */
  240.     }
  241.     
  242.     /* dispose of the threads */
  243.     for (i = 0; i < NTHREADS; i++)
  244.         ThreadEnd(threads[i]);
  245.     ThreadEnd(ThreadMain());
  246.  
  247.     printf("Thread Library: count = %lu (ThreadYield was called %lu times)\n",
  248.         td.count, td.yield);
  249. }
  250.  
  251. /* test Thread Manager */
  252. static void tm_test(void)
  253. {
  254.     ThreadID threads[NTHREADS];
  255.     DataType td;
  256.     ThreadTicksType nextEvent;
  257.     ThreadTicksType stop;
  258.     short i;
  259.     
  260.     printf("\nTesting Thread Manager. This will take %ld seconds.\n", RUNSECS);
  261.  
  262.     /* create several threads */
  263.     memset(&td, 0, sizeof(DataType));
  264.     for (i = 0; i < NTHREADS; i++) {
  265.         OSErr err = NewThread(kCooperativeThread, tm_thread, &td, 0,
  266.                                      kCreateIfNeeded, NULL, threads + i);
  267.         if (err != noErr)
  268.             fatal("can't create thread using Thread Manager", err);
  269.     }
  270.         
  271.     /* run for a predetermined number of ticks */
  272.     nextEvent = 0;
  273.     stop = TickCount() + RUNTICKS;
  274.     while (TickCount() < stop) {
  275.  
  276.         /* periodically discard all pending events */
  277.         if (TickCount() >= nextEvent) {
  278.             FlushEvents(everyEvent, 0);            
  279.             nextEvent = TickCount() + THREAD_TICKS_SEC;
  280.         }
  281.  
  282.         /* switch to another thread */
  283.         td.yield++;
  284.         YieldToAnyThread();
  285.     }
  286.  
  287.     /* dispose of the threads */
  288.     for (i = 0; i < NTHREADS; i++)
  289.         DisposeThread(threads[i], NULL, false);
  290.  
  291.     printf("Thread Manager: count = %lu (YieldToAnyThread was called %lu times)\n",
  292.         td.count, td.yield);
  293. }
  294.  
  295. /* for comparison, test the same operation, but without using threads */
  296. static void nt_test(void)
  297. {
  298.     printf("\nTesting without threads. This will take %ld seconds.\n", RUNSECS);
  299.     printf("No threads: count = %lu\n", nt_thread());
  300. }    
  301.  
  302. void main(void)
  303. {
  304.     long threadsAttr;
  305.     
  306.     HeapInit(0, 0);
  307.     ManagersInit();
  308.     printf("This program compares the efficiency of my Thread Library with\n");
  309.     printf("Apple's Thread Manager. Each test executes for a fixed number of\n");
  310.     printf("seconds. The larger the final count the more efficient the software.\n");
  311.     printf("For best results, don't do anything that could generate any events.\n");
  312.     printf("The entire program should take about %ld seconds to run.\n", RUNSECS * NTESTS);
  313.     printf("This program needs about %ldK to run.\n",
  314.         (ThreadStackDefault() * NTHREADS + 131072L) / 1024);
  315.     tl_test();
  316.     if (Gestalt(gestaltThreadMgrAttr, &threadsAttr) == noErr &&
  317.          (threadsAttr & (1<<gestaltThreadMgrPresent)) != 0)
  318.     {
  319.         tm_test();
  320.     }
  321.     else
  322.         printf("\nCan't test Thread Manager because it isn't installed.\n");
  323.     nt_test();
  324. }
  325.